/*! WOW wow.js - v1.2.1 - 2016-09-05
* https://wowjs.uk
* Copyright (c) 2016 Thomas Grainger; Licensed MIT */

/**
 * WOW.js 是一个用于实现滚动动画效果的JavaScript库
 * 当元素滚动到视图中时，为其添加动画效果
 */

// 模块加载器兼容代码 (AMD, CommonJS, 全局变量)
(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD模块加载器
        define(['module', 'exports'], factory);
    } else if (typeof exports !== 'undefined') {
        // CommonJS模块系统
        factory(module, exports);
    } else {
        // 浏览器全局变量
        var mod = {
            exports: {}
        };
        factory(mod, mod.exports);
        root.WOW = mod.exports;
    }
}(this, function(module, exports) {
    'use strict';
    
    /**
     * 检查构造函数是否被正确调用
     */
    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }
    
    /**
     * 检查元素是否在数组中
     */
    function _indexOf(arr, item) {
        return arr.indexOf(item) >= 0;
    }
    
    /**
     * 合并配置选项，将默认配置与用户配置合并
     */
    function _defaults(obj, defaults) {
        for (var key in defaults) {
            if (obj[key] == null) {
                var value = defaults[key];
                obj[key] = value;
            }
        }
        return obj;
    }
    
    /**
     * 检测是否为移动设备
     */
    function isMobile(agent) {
        return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(agent);
    }
    
    /**
     * 创建自定义事件
     */
    function createEvent(eventName, canBubble, cancelable, detail) {
        var event = void 0;
        if (document.createEvent) {
            event = document.createEvent('CustomEvent');
            event.initCustomEvent(eventName, canBubble, cancelable, detail);
        } else if (document.createEventObject) {
            event = document.createEventObject();
            event.eventType = eventName;
        } else {
            event.eventName = eventName;
        }
        return event;
    }
    
    /**
     * 触发事件
     */
    function triggerEvent(element, event) {
        if (element.dispatchEvent) {
            element.dispatchEvent(event);
        } else if (element[event]) {
            element[event]();
        } else if (element['on' + event]) {
            element['on' + event]();
        }
    }
    
    /**
     * 添加事件监听器
     */
    function addEvent(element, event, listener) {
        if (element.addEventListener) {
            element.addEventListener(event, listener, false);
        } else if (element.attachEvent) {
            element.attachEvent('on' + event, listener);
        } else {
            element[event] = listener;
        }
    }
    
    /**
     * 移除事件监听器
     */
    function removeEvent(element, event, listener) {
        if (element.removeEventListener) {
            element.removeEventListener(event, listener, false);
        } else if (element.detachEvent) {
            element.detachEvent('on' + event, listener);
        } else {
            delete element[event];
        }
    }
    
    /**
     * 获取窗口高度
     */
    function getWindowHeight() {
        return 'innerHeight' in window ? window.innerHeight : document.documentElement.clientHeight;
    }
    
    // 设置模块导出
    Object.defineProperty(exports, '__esModule', {
        value: true
    });
    
    // WeakMap 实现，用于缓存动画名称
    var WeakMap = window.WeakMap || window.MozWeakMap || function() {
        function WeakMapClass() {
            _classCallCheck(this, WeakMapClass);
            this.keys = [];
            this.values = [];
        }
        
        WeakMapClass.prototype.get = function(key) {
            for (var i = 0; i < this.keys.length; i++) {
                var item = this.keys[i];
                if (item === key) {
                    return this.values[i];
                }
            }
        };
        
        WeakMapClass.prototype.set = function(key, value) {
            for (var i = 0; i < this.keys.length; i++) {
                var item = this.keys[i];
                if (item === key) {
                    this.values[i] = value;
                    return this;
                }
            }
            this.keys.push(key);
            this.values.push(value);
            return this;
        };
        
        return WeakMapClass;
    }();
    
    // MutationObserver 实现，用于检测DOM变化
    var MutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver || function() {
        function MutationObserverClass() {
            _classCallCheck(this, MutationObserverClass);
            if (typeof console !== 'undefined' && console !== null) {
                console.warn('MutationObserver is not supported by your browser.');
                console.warn('WOW.js cannot detect dom mutations, please call .sync() after loading new content.');
            }
        }
        
        MutationObserverClass.prototype.observe = function() {};
        MutationObserverClass.notSupported = true;
        
        return MutationObserverClass;
    }();
    
    // 获取计算样式的兼容实现
    var getComputedStyle = window.getComputedStyle || function(element) {
        var regex = /(\-([a-z]){1})/g;
        return {
            getPropertyValue: function(prop) {
                if (prop === 'float') {
                    prop = 'styleFloat';
                }
                
                regex.test(prop) && prop.replace(regex, function(match, group) {
                    return group.toUpperCase();
                });
                
                var currentStyle = element.currentStyle;
                return (currentStyle ? currentStyle[prop] : void 0) || null;
            }
        };
    };
    
    /**
     * WOW类 - 主要的动画控制器
     */
    var WOW = function() {
        function WOW(options) {
            _classCallCheck(this, WOW);
            
            // 默认配置选项
            this.defaults = {
                boxClass: 'wow',         // 带动画的元素的类名
                animateClass: 'animated', // animate.css中的动画类名
                offset: 0,               // 元素距离视图底部多少像素时触发动画
                mobile: true,            // 是否在移动设备上显示动画
                live: true,              // 是否监听DOM变化，自动为新加的元素添加动画
                callback: null,          // 动画触发后的回调函数
                scrollContainer: null    // 滚动容器，如果不是window
            };
            
            // 动画帧请求，优先使用requestAnimationFrame
            this.animate = function() {
                return 'requestAnimationFrame' in window ? function(callback) {
                    return window.requestAnimationFrame(callback);
                } : function(callback) {
                    return callback();
                };
            }();
            
            // 浏览器前缀
            this.vendors = ['moz', 'webkit'];
            
            // 绑定this到方法
            this.start = this.start.bind(this);
            this.resetAnimation = this.resetAnimation.bind(this);
            this.scrollHandler = this.scrollHandler.bind(this);
            this.scrollCallback = this.scrollCallback.bind(this);
            
            // 滚动状态标志
            this.scrolled = true;
            
            // 合并用户配置和默认配置
            this.config = _defaults(options || {}, this.defaults);
            
            // 设置滚动容器
            if (options && options.scrollContainer) {
                this.config.scrollContainer = document.querySelector(options.scrollContainer);
            }
            
            // 创建动画名称缓存和自定义事件
            this.animationNameCache = new WeakMap();
            this.wowEvent = createEvent(this.config.boxClass);
        }
        
        /**
         * 初始化WOW实例
         */
        WOW.prototype.init = function() {
            // 设置元素为文档根元素
            this.element = window.document.documentElement;
            
            // 如果文档已加载完成，则启动动画，否则等待DOMContentLoaded事件
            if (_indexOf(document.readyState, ['interactive', 'complete'])) {
                this.start();
            } else {
                addEvent(document, 'DOMContentLoaded', this.start);
            }
            
            // 已完成动画的元素数组
            this.finished = [];
        };
        
        /**
         * 开始监听和显示动画
         */
        WOW.prototype.start = function() {
            var _this = this;
            
            // 重置停止标志
            this.stopped = false;
            
            // 获取所有需要动画的元素
            this.boxes = [].slice.call(this.element.querySelectorAll('.' + this.config.boxClass));
            
            // 保存所有元素的副本
            this.all = this.boxes.slice(0);
            
            // 如果有元素需要动画
            if (this.boxes.length) {
                if (this.disabled()) {
                    // 如果在移动设备上禁用了动画，则重置样式
                    this.resetStyle();
                } else {
                    // 否则为每个元素应用初始样式
                    for (var i = 0; i < this.boxes.length; i++) {
                        var box = this.boxes[i];
                        this.applyStyle(box, true);
                    }
                }
            }
            
            // 如果动画未被禁用
            if (!this.disabled()) {
                // 添加滚动和调整大小事件监听器
                addEvent(this.config.scrollContainer || window, 'scroll', this.scrollHandler);
                addEvent(window, 'resize', this.scrollHandler);
                
                // 设置定时器检查滚动
                this.interval = setInterval(this.scrollCallback, 50);
            }
            
            // 如果启用了实时监听
            if (this.config.live) {
                // 创建MutationObserver监听DOM变化
                var mutationObserver = new MutationObserver(function(mutations) {
                    for (var i = 0; i < mutations.length; i++) {
                        var mutation = mutations[i];
                        for (var j = 0; j < mutation.addedNodes.length; j++) {
                            var node = mutation.addedNodes[j];
                            _this.doSync(node);
                        }
                    }
                });
                
                // 监听body的子元素变化
                mutationObserver.observe(document.body, {
                    childList: true,
                    subtree: true
                });
            }
        };
        
        /**
         * 停止动画和事件监听
         */
        WOW.prototype.stop = function() {
            this.stopped = true;
            
            // 移除事件监听器
            removeEvent(this.config.scrollContainer || window, 'scroll', this.scrollHandler);
            removeEvent(window, 'resize', this.scrollHandler);
            
            // 清除定时器
            if (this.interval !== null) {
                clearInterval(this.interval);
            }
        };
        
        /**
         * 同步DOM变化（用于不支持MutationObserver的浏览器）
         */
        WOW.prototype.sync = function() {
            if (MutationObserver.notSupported) {
                this.doSync(this.element);
            }
        };
        
        /**
         * 执行同步操作，查找新添加的元素
         */
        WOW.prototype.doSync = function(element) {
            if (typeof element !== 'undefined' && element !== null) {
                element = element.parentNode || element;
                
                // 查找所有新添加的wow元素
                var nodes = element.querySelectorAll('.' + this.config.boxClass);
                for (var i = 0; i < nodes.length; i++) {
                    var node = nodes[i];
                    
                    // 如果元素不在all数组中，添加到boxes和all数组
                    if (!_indexOf(node, this.all)) {
                        this.boxes.push(node);
                        this.all.push(node);
                        
                        // 根据状态应用样式
                        if (this.stopped || this.disabled()) {
                            this.resetStyle();
                        } else {
                            this.applyStyle(node, true);
                        }
                        
                        // 触发滚动检查
                        this.scrolled = true;
                    }
                }
            }
        };
        
        /**
         * 显示元素动画
         */
        WOW.prototype.show = function(box) {
            // 应用样式
            this.applyStyle(box);
            
            // 添加动画类
            box.className = box.className + ' ' + this.config.animateClass;
            
            // 执行回调
            if (this.config.callback !== null) {
                this.config.callback(box);
            }
            
            // 触发自定义事件
            triggerEvent(box, this.wowEvent);
            
            // 添加动画结束事件监听器
            addEvent(box, 'animationend', this.resetAnimation);
            addEvent(box, 'oanimationend', this.resetAnimation);
            addEvent(box, 'webkitAnimationEnd', this.resetAnimation);
            addEvent(box, 'MSAnimationEnd', this.resetAnimation);
            
            return box;
        };
        
        /**
         * 应用样式到元素
         */
        WOW.prototype.applyStyle = function(box, hidden) {
            var _this = this;
            
            // 获取自定义属性
            var duration = box.getAttribute('data-wow-duration');
            var delay = box.getAttribute('data-wow-delay');
            var iteration = box.getAttribute('data-wow-iteration');
            
            // 使用动画帧应用样式
            return this.animate(function() {
                return _this.customStyle(box, hidden, duration, delay, iteration);
            });
        };
        
        /**
         * 重置元素样式
         */
        WOW.prototype.resetStyle = function() {
            for (var i = 0; i < this.boxes.length; i++) {
                var box = this.boxes[i];
                box.style.visibility = 'visible';
            }
        };
        
        /**
         * 动画结束时重置动画类
         */
        WOW.prototype.resetAnimation = function(event) {
            if (event.type.toLowerCase().indexOf('animationend') >= 0) {
                var target = event.target || event.srcElement;
                target.className = target.className.replace(this.config.animateClass, '').trim();
            }
        };
        
        /**
         * 应用自定义样式到元素
         */
        WOW.prototype.customStyle = function(box, hidden, duration, delay, iteration) {
            // 缓存动画名称
            if (hidden) {
                this.cacheAnimationName(box);
            }
            
            // 设置可见性
            box.style.visibility = hidden ? 'hidden' : 'visible';
            
            // 设置持续时间
            if (duration) {
                this.vendorSet(box.style, {
                    animationDuration: duration
                });
            }
            
            // 设置延迟
            if (delay) {
                this.vendorSet(box.style, {
                    animationDelay: delay
                });
            }
            
            // 设置迭代次数
            if (iteration) {
                this.vendorSet(box.style, {
                    animationIterationCount: iteration
                });
            }
            
            // 设置动画名称
            this.vendorSet(box.style, {
                animationName: hidden ? 'none' : this.cachedAnimationName(box)
            });
            
            return box;
        };
        
        /**
         * 设置带浏览器前缀的CSS属性
         */
        WOW.prototype.vendorSet = function(elemStyle, prop) {
            for (var style in prop) {
                if (prop.hasOwnProperty(style)) {
                    var value = prop[style];
                    elemStyle[style] = value;
                    
                    // 添加浏览器前缀版本
                    for (var i = 0; i < this.vendors.length; i++) {
                        var vendor = this.vendors[i];
                        elemStyle[vendor + style.charAt(0).toUpperCase() + style.substr(1)] = value;
                    }
                }
            }
        };
        
        /**
         * 获取带浏览器前缀的CSS属性值
         */
        WOW.prototype.vendorCSS = function(element, prop) {
            var style = getComputedStyle(element);
            var cssValue = style.getPropertyCSSValue(prop);
            
            // 尝试获取带浏览器前缀的值
            for (var i = 0; i < this.vendors.length; i++) {
                var vendor = this.vendors[i];
                cssValue = cssValue || style.getPropertyCSSValue('-' + vendor + '-' + prop);
            }
            
            return cssValue;
        };
        
        /**
         * 获取元素的动画名称
         */
        WOW.prototype.animationName = function(element) {
            var animationName = void 0;
            
            try {
                animationName = this.vendorCSS(element, 'animation-name').cssText;
            } catch (e) {
                // 降级处理
                animationName = getComputedStyle(element).getPropertyValue('animation-name');
            }
            
            return animationName === 'none' ? '' : animationName;
        };
        
        /**
         * 缓存元素的动画名称
         */
        WOW.prototype.cacheAnimationName = function(element) {
            return this.animationNameCache.set(element, this.animationName(element));
        };
        
        /**
         * 获取缓存的动画名称
         */
        WOW.prototype.cachedAnimationName = function(element) {
            return this.animationNameCache.get(element);
        };
        
        /**
         * 滚动事件处理函数
         */
        WOW.prototype.scrollHandler = function() {
            this.scrolled = true;
        };
        
        /**
         * 滚动回调，检查元素是否可见
         */
        WOW.prototype.scrollCallback = function() {
            if (this.scrolled) {
                this.scrolled = false;
                
                var i = 0;
                var boxes = [];
                
                // 遍历所有元素
                for (i = 0; i < this.boxes.length; i++) {
                    var box = this.boxes[i];
                    
                    if (box) {
                        // 检查元素是否可见
                        if (this.isVisible(box)) {
                            // 显示元素
                            this.show(box);
                            continue;
                        }
                        // 将不可见的元素保留
                        boxes.push(box);
                    }
                }
                
                // 更新boxes数组
                this.boxes = boxes;
                
                // 如果没有更多元素需要动画且不监听新元素，则停止
                if (!this.boxes.length && !this.config.live) {
                    this.stop();
                }
            }
        };
        
        /**
         * 获取元素距离文档顶部的偏移量
         */
        WOW.prototype.offsetTop = function(element) {
            // 处理特殊情况
            while (element.offsetTop === undefined) {
                element = element.parentNode;
            }
            
            var top = element.offsetTop;
            
            // 累加所有父元素的偏移量
            while (element.offsetParent) {
                element = element.offsetParent;
                top += element.offsetTop;
            }
            
            return top;
        };
        
        /**
         * 检查元素是否在视图中可见
         */
        WOW.prototype.isVisible = function(element) {
            // 获取自定义偏移量或使用配置中的偏移量
            var offset = element.getAttribute('data-wow-offset') || this.config.offset;
            
            // 获取滚动位置
            var scrollTop = this.config.scrollContainer ? this.config.scrollContainer.scrollTop : window.pageYOffset;
            
            // 计算触发点
            var viewBottom = scrollTop + Math.min(this.element.clientHeight, getWindowHeight()) - offset;
            
            // 获取元素位置
            var top = this.offsetTop(element);
            var bottom = top + element.clientHeight;
            
            // 检查元素是否可见
            return bottom <= viewBottom && top >= scrollTop;
        };
        
        /**
         * 检查是否应禁用动画（在移动设备上）
         */
        WOW.prototype.disabled = function() {
            // 如果配置了禁用移动设备且当前是移动设备，则返回true
            return !this.config.mobile && isMobile(navigator.userAgent);
        };
        
        return WOW;
    }();
    
    // 导出WOW类
    exports.default = WOW;
    module.exports = exports.default;
}));